package com.wechat.pay.java.service.partnerpayments.wxcodepay;

import com.wechat.pay.java.service.partnerpayments.jsapi.model.QueryOrderByIdRequest;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import com.wechat.pay.java.service.partnerpayments.wxcodepay.model.PrepayRequest;
import com.wechat.pay.java.service.partnerpayments.wxcodepay.model.PrepayResponse;
import com.wechat.pay.java.service.partnerpayments.wxcodepay.model.CloseOrderRequest;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.http.*;
import com.wechat.pay.java.core.util.GsonUtil;

import java.util.Objects;

public class CodePayService {
    private final HttpClient httpClient;
    private final HostName hostName;

    private CodePayService(HttpClient httpClient, HostName hostName) {
        this.httpClient = (HttpClient) Objects.requireNonNull(httpClient);
        this.hostName = hostName;
    }

    public PrepayResponse prepay(PrepayRequest request) {
        String requestPath = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/codepay";
        if (this.hostName != null) {
            requestPath = requestPath.replaceFirst(HostName.API.getValue(), this.hostName.getValue());
        }

        HttpHeaders headers = new HttpHeaders();
        headers.addHeader("Accept", MediaType.APPLICATION_JSON.getValue());
        headers.addHeader("Content-Type", MediaType.APPLICATION_JSON.getValue());
        HttpRequest httpRequest = (new HttpRequest.Builder()).httpMethod(HttpMethod.POST).url(requestPath).headers(headers).body(this.createRequestBody(request)).build();
        HttpResponse<PrepayResponse> httpResponse = this.httpClient.execute(httpRequest, PrepayResponse.class);
        return (PrepayResponse)httpResponse.getServiceResponse();
    }

    public void closeOrder(CloseOrderRequest request) {
        String requestPath = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/{out_trade_no}/reverse";
        requestPath = requestPath.replace("{out_trade_no}", UrlEncoder.urlEncode(request.getOutTradeNo()));
        if (this.hostName != null) {
            requestPath = requestPath.replaceFirst(HostName.API.getValue(), this.hostName.getValue());
        }

        HttpHeaders headers = new HttpHeaders();
        headers.addHeader("Accept", MediaType.APPLICATION_JSON.getValue());
        headers.addHeader("Content-Type", MediaType.APPLICATION_JSON.getValue());
        HttpRequest httpRequest = (new HttpRequest.Builder()).httpMethod(HttpMethod.POST).url(requestPath).headers(headers).body(this.createRequestBody(request)).build();
        this.httpClient.execute(httpRequest, (Class)null);
    }

    public Transaction queryOrderById(QueryOrderByIdRequest request) {
        String requestPath = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/id/{transaction_id}";
        requestPath = requestPath.replace("{transaction_id}", UrlEncoder.urlEncode(request.getTransactionId()));
        QueryParameter queryParameter = new QueryParameter();
        if (request.getSpMchid() != null) {
            queryParameter.add("sp_mchid", UrlEncoder.urlEncode(request.getSpMchid()));
        }

        if (request.getSubMchid() != null) {
            queryParameter.add("sub_mchid", UrlEncoder.urlEncode(request.getSubMchid()));
        }

        requestPath = requestPath + queryParameter.getQueryStr();
        if (this.hostName != null) {
            requestPath = requestPath.replaceFirst(HostName.API.getValue(), this.hostName.getValue());
        }

        HttpHeaders headers = new HttpHeaders();
        headers.addHeader("Accept", MediaType.APPLICATION_JSON.getValue());
        headers.addHeader("Content-Type", MediaType.APPLICATION_JSON.getValue());
        HttpRequest httpRequest = (new HttpRequest.Builder()).httpMethod(HttpMethod.GET).url(requestPath).headers(headers).build();
        HttpResponse<Transaction> httpResponse = this.httpClient.execute(httpRequest, Transaction.class);
        return (Transaction)httpResponse.getServiceResponse();
    }

    public Transaction queryOrderByOutTradeNo(QueryOrderByOutTradeNoRequest request) {
        String requestPath = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/{out_trade_no}";
        requestPath = requestPath.replace("{out_trade_no}", UrlEncoder.urlEncode(request.getOutTradeNo()));
        QueryParameter queryParameter = new QueryParameter();
        if (request.getSpMchid() != null) {
            queryParameter.add("sp_mchid", UrlEncoder.urlEncode(request.getSpMchid()));
        }

        if (request.getSubMchid() != null) {
            queryParameter.add("sub_mchid", UrlEncoder.urlEncode(request.getSubMchid()));
        }

        requestPath = requestPath + queryParameter.getQueryStr();
        if (this.hostName != null) {
            requestPath = requestPath.replaceFirst(HostName.API.getValue(), this.hostName.getValue());
        }

        HttpHeaders headers = new HttpHeaders();
        headers.addHeader("Accept", MediaType.APPLICATION_JSON.getValue());
        headers.addHeader("Content-Type", MediaType.APPLICATION_JSON.getValue());
        HttpRequest httpRequest = (new HttpRequest.Builder()).httpMethod(HttpMethod.GET).url(requestPath).headers(headers).build();
        HttpResponse<Transaction> httpResponse = this.httpClient.execute(httpRequest, Transaction.class);
        return (Transaction)httpResponse.getServiceResponse();
    }


    private RequestBody createRequestBody(Object request) {
        return (new JsonRequestBody.Builder()).body(GsonUtil.toJson(request)).build();
    }

    public static class Builder {
        private HttpClient httpClient;
        private HostName hostName;

        public Builder() {
        }

        public CodePayService.Builder config(Config config) {
            this.httpClient = (new DefaultHttpClientBuilder()).config(config).build();
            return this;
        }

        public CodePayService.Builder hostName(HostName hostName) {
            this.hostName = hostName;
            return this;
        }

        public CodePayService.Builder httpClient(HttpClient httpClient) {
            this.httpClient = httpClient;
            return this;
        }

        public CodePayService build() {
            return new CodePayService(this.httpClient, this.hostName);
        }
    }
}
